
#include "resourcemanager.h"
#include "log.h"
#include "strutil.h"
#include "unit.h"
#include "frameanimation.h"
#include "gamemanager.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
#import <Foundation/Foundation.h>
#endif

#define a2f(s)				static_cast<float>(atof(s))

const string RES_PATH = "textures/";
const char* DEFAULT_TEXTURE_NAME = "textures/default.png";

ResourceManager* ResourceManager::_instance = nullptr;


ResourceManager::ResourceManager() {
}

ResourceManager::~ResourceManager() {
}

ResourceManager* ResourceManager::getInstance(){
    if(_instance == nullptr){
        _instance = new ResourceManager();
    }

    return _instance;
}

bool ResourceManager::hasAnimationInfo( const string& key) {
	return _mapAnimationInfo.find(key) != _mapAnimationInfo.end();
}

void ResourceManager::loadStrings(){
    ResourceManager::loadLocaleString("public/config/values", "strings.xml","us", false);
}

string ResourceManager::loadTextFile(const string& fileName, bool absolute) {
    unsigned long length;
    char* str = (char*)CCFileUtils::sharedFileUtils()->getFileData(fileName.c_str(), "rb", &length);
    string s = decodeBuffer(str, length);
    delete[] str;
    return s;
}

void ResourceManager::loadLocaleString(const char* rootPath, const char* filename, const char* locale, bool absolutePath) {
    using namespace tinyxml2;
    tinyxml2::XMLDocument doc;

    string filepath(string(rootPath)+"/"+locale+"/"+filename);

    string content = loadTextFile(filepath.c_str(), absolutePath);
    doc.Parse(content.c_str());

    XMLElement* pData = doc.FirstChildElement("resources");
    XMLElement* pItem = pData->FirstChildElement("string");
    while (pItem) {
        string name = pItem->Attribute("name");
        string value = "";
        const char* s = pItem->GetText();
        if(s) {
            value = s;
        }
        _mapResource.insert(make_pair(name, value));
        pItem = pItem ->NextSiblingElement("string");
    }
}


const char* ResourceManager::getValue(const char* key) {
    map<string, string>::iterator ite = _mapResource.find(key);
    return ite==_mapResource.end()?"":ite->second.c_str();
}

AnimationInfo ResourceManager::getAnimationInfo(string const & key) {
	if (_mapAnimationInfo.find(key) != _mapAnimationInfo.end()) {
		return _mapAnimationInfo[key];
	}

	_DHLOG("*** error: not found animation info:%s ***", key.c_str());

	AnimationInfo info;
	return info;
}

string ResourceManager::decodeBuffer(const char* str, int length) {
    string s;
#ifndef DH_ENCODE
	s = str;
#else
	char* buf = new char[length + 1];
	memset(buf, 0, length + 1);
	memcpy(buf, str, length);
    s = buf;
    int lengthOfKey = 19;
    char encryptChars[] = { 0x1c,0xa3,0x4b,0x12,0x77,0x84,0xaa,
        0x3b,0x2b,0xb2,0x23,0x7c,0xee,0xef,0xf2,0xa0,
        0x3b,0x2b,0xcc};
    int index=0;
    int count = 0;
    while(count<length) {
        buf[count++]^=encryptChars[index++];
        if (index==lengthOfKey) {
            index=8;
        }
    }
    s= buf;
    delete[] buf;
#endif
    return s;
}

CCSprite *ResourceManager::makeSprite(const string& name) {
    CCSprite *spr = CCSprite::createWithSpriteFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(name.c_str()));
    if(spr == nullptr) {
        spr = CCSprite::create(name.c_str());
    }

    if(spr == nullptr) {
        _DHLOG("texture not found: %s", name.c_str());
        spr = CCSprite::create(DEFAULT_TEXTURE_NAME);
    }

    spr->setScale(GM->minscale);
    return spr;
}

CCSprite *ResourceManager::makeSpriteFromResource(const string& name) {
    return CCSprite::create((RES_PATH + name).c_str());
}

CCSpriteFrame *ResourceManager::makeSpriteFrame(const string& name) {
    CCSpriteFrame *f = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(name.c_str());
    if(f == nullptr) {
        _DHLOG("texture not found: %s", name.c_str());
        CCSprite *spr = CCSprite::create(DEFAULT_TEXTURE_NAME);
        f = spr->displayFrame();
    }

    return f;
}

FrameAnimation * ResourceManager::createAnimation(const string& animKey, const CCPoint& location) {
    AnimationInfo info = getAnimationInfo(animKey);

    FrameAnimation * anim = new FrameAnimation();
    anim->init();
    anim->set(info);
    anim->setScale(GM->minscale);
    anim->setLogicPosition(location);
	anim->autorelease();

    return anim;
}

void ResourceManager::removeTextureForKey(const string& file) {
    CCTextureCache::sharedTextureCache()->removeTextureForKey((RES_PATH + file).c_str());
}

void ResourceManager::removeSpriteFramesFromFile(const string& file) {
    CCSpriteFrameCache::sharedSpriteFrameCache()->removeSpriteFramesFromFile((RES_PATH + "z_res/" + file).c_str());
}

void ResourceManager::loadTexturesFromPList(const string& fn, bool absolutePath) {
    static string rootpath = RES_PATH;
    if (absolutePath) {
        CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(fn.c_str());
    } else {
        CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile((rootpath + fn).c_str());
    }
}

CCMenuItemSprite *ResourceManager::makeButton(const char *nameNormal, const char *nameSelected, const char *nameDisabled, CCNode *target, SEL_MenuHandler selector, CCNode *parent) {
    CCSprite *s1 = makeSprite(nameNormal);
    CCSprite *s2 = nameSelected ? makeSprite(nameSelected) : nullptr;
    CCSprite *s3 = nameDisabled ? makeSprite(nameDisabled) : nullptr;

    s1->setScale(1.0f);
    if(s2){
        s2->setScale(1.0f);
    }
    if(s3){
        s3->setScale(1.0f);
    }

    CCMenuItemSprite *button = CCMenuItemSprite::create(s1, s2, s3, target, selector);
    button->setScale(GM->minscale);

    CCMenu *menu = CCMenu::create(button, nullptr);
    menu->setPosition(CCPointZero);
    if (parent) {
        parent->addChild(menu);
        if (parent != target) {
            button->setScale(1.0f);
        }
    } else {
        target->addChild(menu);
    }
    return button;
}

CCMenuItemSprite *ResourceManager::makeBackButton(CCNode *target, SEL_MenuHandler selector, CCNode *parent, bool isRoundButton) {
	CCSprite *s1 = makeSprite(isRoundButton ? "button/round_back_0.png" : "button/back_1.png");
	CCSprite *s2 = makeSprite(isRoundButton ? "button/round_back_1.png" : "button/back_0.png");
	CCSprite *s3 = nullptr;

	s1->setScale(1.0f);
	s2->setScale(1.0f);

	CCMenuItemSprite *button = CCMenuItemSprite::create(s1, s2, s3, target, selector);
	button->setScale(GM->minscale);

	CCMenu *menu = CCMenu::create(button, nullptr);
	menu->setPosition(CCPointZero);
	if (parent) {
		parent->addChild(menu);
		if (parent != target) {
			button->setScale(1.0f);
		}
	} else {
		target->addChild(menu);
	}
	return button;
}

void ResourceManager::loadAnimationData() {
	using namespace tinyxml2;

	string xml = ResourceManager::loadTextFile("data/animations.xml");

	tinyxml2::XMLDocument doc;
	doc.Parse(xml.c_str());

	XMLElement* root = doc.FirstChildElement("data");
	XMLElement* item = root->FirstChildElement("i");
	while(item){
		AnimationInfo info;
		info.key = item->Attribute("k");
		info.frameRate = item->IntAttribute("frame_rate") / 1000.0f;

		XMLElement* xeFrames = item->FirstChildElement("f");
		while(xeFrames) {
			info.frames.push_back(xeFrames->GetText());
			xeFrames = xeFrames->NextSiblingElement("f");
		}

		_mapAnimationInfo.insert(make_pair(info.key, info));

		item = item->NextSiblingElement("i");
	}
}

void ResourceManager::init() {
	loadAnimationData();
}

CCLabelTTF *ResourceManager::makeButtonLabel(const char *text, int fontSize, CCNode *parent) {
	CCSize size = parent->getContentSize();
	CCLabelTTF * label = CCLabelTTF::create(text, "", fontSize);
	label->setPosition(ccp(size.width / 2.0f, size.height / 2.0f));
	label->setAnchorPoint(ccp(0.5f, 0.5f));
	parent->addChild(label);

	return label;
}
